#pragma once

HRESULT hr;

struct SVector2f
{
	float r; // Rayleigh
	float m; // Mie
};
typedef SVector2f Vec2f;

struct ScbConstant
{
	float PI;
	float InnerRadius;
	float OuterRadius;
	float fScale;
	float KrESun;
	float KmESun;
	float Kr4PI;
	float Km4PI;
	int tNumSamples;
	int iNumSamples;
	D3DXVECTOR2 v2dRayleighMieScaleHeight;
	D3DXVECTOR3 WavelengthMie;
	float InvOpticalDepthN;
	D3DXVECTOR3 v3HG;
	float InvOpticalDepthNLessOne;
	D3DXVECTOR2 InvRayleighMieN;
	D3DXVECTOR2 InvRayleighMieNLessOne;
	float HalfTexelOpticalDepthN;
	D3DXVECTOR3 InvWavelength4;
};

class CSky
{
	ScbConstant cbConstant;

	ID3D10Device *							pDEV;
	ID3DX10Mesh *							pDome;
	ID3D10Effect *							pEffect;
	ID3D10EffectTechnique *					pTechniqueRender;
	ID3D10EffectTechnique *					pTechniqueUpdate;
	ID3D10EffectTechnique *					pTechniqueUpdateFast;
	ID3D10EffectTechnique *					pTechniqueCreateOpticalDepth;
	ID3D10EffectMatrixVariable *			pWorldViewProjVar;
	ID3D10EffectVectorVariable *			pv3SunDirVar;
	ID3D10EffectConstantBuffer *			pConstantBuffercbConstantVar;
	ID3D10Buffer *							pConstantBuffercbConstant;
	ID3D10Buffer *							pVertexBufferRT;
	ID3D10EffectShaderResourceVariable *	pMieVar;
	ID3D10EffectShaderResourceVariable *	pRayleighVar;
	ID3D10EffectShaderResourceVariable *	pOpticalDepthVar;
	ID3D10ShaderResourceView *				pMieSRV;
	ID3D10ShaderResourceView *				pRayleighSRV;
	ID3D10ShaderResourceView *				pOpticalDepthSRV;
	ID3D10InputLayout *						pDomeVertexLayout;
	ID3D10InputLayout *						pRTVertexLayout;
	ID3D10Texture2D	*						pMie;
	ID3D10Texture2D	*						pRayleigh;
	ID3D10Texture2D	*						pOpticalDepth;
	ID3D10RenderTargetView *				pMieRTV;
	ID3D10RenderTargetView *				pRayleighRTV;
	ID3D10RenderTargetView *				pOpticalDepthRTV;

	UINT DomeN;
	UINT OpticalDepthN;
	UINT RayleighMieN;
	D3DXVECTOR3 v3SunDir;
	
	struct DomeVertex
	{
		D3DXVECTOR3 v;
		D3DXVECTOR2 uv;
	};

	struct RTVertex
	{
		D3DXVECTOR2 v;
	};

	bool HitOuterSphere( D3DXVECTOR3 O, D3DXVECTOR3 Dir, double & t ) 
	{
		const D3DXVECTOR3 L = -O;

		double B = D3DXVec3Dot(&L , &Dir);
		double C = D3DXVec3Dot(&L , &L);
		//if( B < 1e-5f && C > cbConstant.OuterRadius * cbConstant.OuterRadius ) return false;

		double D = C - B * B; 
		//if( D > cbConstant.OuterRadius * cbConstant.OuterRadius ) return false;
		
		double q = sqrt( cbConstant.OuterRadius * cbConstant.OuterRadius - D );
		t = B;
		/*if( C > cbConstant.OuterRadius * cbConstant.OuterRadius )  
			t -= q;
		else*/
			t += q;

		return true;
	}

	void CreateDome( )
	{
		UINT Latitude = DomeN/2;
		UINT Longitude = DomeN;
		//number of vertices
		UINT DVSize = Longitude * Latitude;
		//number of indices
		UINT DISize = (Longitude - 1) * (Latitude - 1) * 2;
		DVSize *= 2;
		DISize *= 2;

		D3D10_INPUT_ELEMENT_DESC Dome_layout[] =
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT   , 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		};

		// Create Mesh
		V( D3DX10CreateMesh(
			pDEV,
			Dome_layout,
			sizeof( Dome_layout ) / sizeof( Dome_layout[0] ),
			Dome_layout[0].SemanticName,
			DVSize,
			DISize,
			0,
			&pDome) );

		// CreateVertexbuffer
		ID3DX10MeshBuffer * Dx10MeshBuffer;
		V( pDome->GetVertexBuffer( 0, &Dx10MeshBuffer ) );

		SIZE_T MeshBufferSize;
		DomeVertex * pVertices;
		V( Dx10MeshBuffer->Map( (VOID**)&pVertices, &MeshBufferSize ) );
	
		UINT DomeIndex = 0;
		for( int i = 0; i < Longitude; i++ )
		{
			//const double dDistribution = (1.0 - exp(-0.5 * (i*10.0) / double(DomeN - 1)));
			//const double MoveXZ = 100.0 * dDistribution * D3DX_PI / 180.0;
			const double MoveXZ = 100.0 * ( i / float(Longitude - 1) ) * D3DX_PI / 180.0;

			for( int j = 0; j < Latitude; j++ )
			{	
				//const double MoveY = (D3DX_PI * 2.0) * j / (DomeN - 1) ;
				const double MoveY = D3DX_PI * j / (Latitude - 1) ;

				pVertices[DomeIndex].v.x = sin( MoveXZ ) * cos( MoveY  );
				pVertices[DomeIndex].v.y = cos( MoveXZ );
				pVertices[DomeIndex].v.z = sin( MoveXZ ) * sin( MoveY  );

				pVertices[DomeIndex].v *= 10.0f;

				pVertices[DomeIndex].uv.x = 0.5 / float( Longitude ) + i / float( Longitude );	// [0.5, Texsize-0.5] 
				pVertices[DomeIndex].uv.y = 0.5 / float( Latitude ) + j / float( Latitude );	// [0.5, Texsize-0.5]

				DomeIndex++;
			}
		}

		for( int i = 0; i < Longitude; i++ )
		{
			//const double dDistribution = (1.0 - exp(-0.5 * (i*10.0) / double(DomeN - 1)));
			//const double MoveXZ = 100.0 * dDistribution * D3DX_PI / 180.0;
			const double MoveXZ = 100.0 * ( i / float(Longitude - 1) ) * D3DX_PI / 180.0;

			for( int j = 0; j < Latitude; j++ )
			{	
				//const double MoveY = (D3DX_PI * 2.0) * j / (DomeN - 1) ;
				const double MoveY = (D3DX_PI * 2.0) - (D3DX_PI * j / (Latitude - 1)) ;

				pVertices[DomeIndex].v.x = sin( MoveXZ ) * cos( MoveY  );
				pVertices[DomeIndex].v.y = cos( MoveXZ );
				pVertices[DomeIndex].v.z = sin( MoveXZ ) * sin( MoveY  );

				pVertices[DomeIndex].v *= 10.0f;

				pVertices[DomeIndex].uv.x = 0.5 / float( Longitude ) + i / float( Longitude );	// [0.5, Texsize-0.5] 
				pVertices[DomeIndex].uv.y = 0.5 / float( Latitude ) + j / float( Latitude );	// [0.5, Texsize-0.5]

				DomeIndex++;
			}
		}

		V( Dx10MeshBuffer->Unmap() );
		SAFE_RELEASE( Dx10MeshBuffer );

		// CreateIndexBuffer
		V( pDome->GetIndexBuffer( &Dx10MeshBuffer ) );

		unsigned short * pIndices = NULL;
		V( Dx10MeshBuffer->Map( (VOID**)&pIndices, &MeshBufferSize ) );

		for( unsigned short i = 0; i < Longitude - 1; i++)
		{
			for( unsigned short j = 0; j < Latitude - 1; j++)
			{
				*(pIndices++) = i * Latitude + j;
				*(pIndices++) = (i + 1) * Latitude + j;
				*(pIndices++) = (i + 1) * Latitude + j + 1;

				*(pIndices++) = (i + 1) * Latitude + j + 1;
				*(pIndices++) = i * Latitude + j + 1;
				*(pIndices++) = i * Latitude + j;
			}
		}

		const UINT Offset = Latitude * Longitude;
		for( unsigned short i = 0; i < Longitude - 1; i++)
		{
			for( unsigned short j = 0; j < Latitude - 1; j++)
			{
				*(pIndices++) = Offset + i * Latitude + j;
				*(pIndices++) = Offset + (i + 1) * Latitude + j + 1;
				*(pIndices++) = Offset + (i + 1) * Latitude + j;

				*(pIndices++) = Offset + i * Latitude + j + 1;
				*(pIndices++) = Offset + (i + 1) * Latitude + j + 1;
				*(pIndices++) = Offset + i * Latitude + j;
			}
		}

		V( Dx10MeshBuffer->Unmap() );
		SAFE_RELEASE( Dx10MeshBuffer );

		// Commit to GPU
		V( pDome->CommitToDevice() );

		// Create the input layout
		D3D10_PASS_DESC PassDesc;
		pTechniqueRender->GetPassByIndex( 0 )->GetDesc( &PassDesc );
		V( pDEV->CreateInputLayout( Dome_layout, sizeof(Dome_layout) / sizeof( Dome_layout[0] ), PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &pDomeVertexLayout ) );
	}

public :
	CSky( ID3D10Device * pd3dDevice )
	{
		pDEV = pd3dDevice;
		//--------------------------------------------------------------------------------------
		// Constants
		//--------------------------------------------------------------------------------------

		cbConstant.PI = D3DX_PI;
		double ESun = 20.0;
		double Kr = 0.0025;
		double Km = 0.0010;
		cbConstant.KrESun = Kr * ESun;
		cbConstant.KmESun = Km * ESun;
		cbConstant.Kr4PI = Kr * 4.0 * cbConstant.PI;
		cbConstant.Km4PI = Km * 4.0 * cbConstant.PI;
		cbConstant.InnerRadius = 6356.7523142;
		cbConstant.OuterRadius = cbConstant.InnerRadius * 1.0157313; // karman line
		cbConstant.fScale = 1.0 / (cbConstant.OuterRadius - cbConstant.InnerRadius);
		cbConstant.v2dRayleighMieScaleHeight.x = 0.25; 
		cbConstant.v2dRayleighMieScaleHeight.y = 0.1;

		cbConstant.InvWavelength4.x = 1.0 / pow( 0.650, 4 );
		cbConstant.InvWavelength4.y = 1.0 / pow( 0.570, 4 );
		cbConstant.InvWavelength4.z = 1.0 / pow( 0.475, 4 );
		cbConstant.WavelengthMie.x = pow( 0.650, -0.84 );
		cbConstant.WavelengthMie.y = pow( 0.570, -0.84 );
		cbConstant.WavelengthMie.z = pow( 0.475, -0.84 );

		double g = -0.995;
		double g2 = g * g;
		cbConstant.v3HG.x = 1.5f * ( (1.0f - g2) / (2.0f + g2) );
		cbConstant.v3HG.y = 1.0f + g2;
		cbConstant.v3HG.z = 2.0f * g;

		cbConstant.tNumSamples = 50;
		cbConstant.iNumSamples = 20;
		DomeN = 128;
		OpticalDepthN = 256;
		RayleighMieN = 256;

		cbConstant.InvOpticalDepthN = 1.0 / float( OpticalDepthN );
		cbConstant.InvOpticalDepthNLessOne = 1.0 / float( OpticalDepthN - 1 );
		cbConstant.HalfTexelOpticalDepthN = 0.5 / float( OpticalDepthN );
		cbConstant.InvRayleighMieN = D3DXVECTOR2( 1.0f / float( RayleighMieN ), 1.0f / float( RayleighMieN / 2 ) );
		cbConstant.InvRayleighMieNLessOne = D3DXVECTOR2( 1.0f / float( RayleighMieN - 1 ), 1.0f / float( RayleighMieN / 2 - 1) );
		v3SunDir = D3DXVECTOR3( 0, 1, 0 );

		//--------------------------------------------------------------------------------------
		// Create cbuffer
		//--------------------------------------------------------------------------------------
		D3D10_BUFFER_DESC cbDesc;
		cbDesc.ByteWidth = sizeof( ScbConstant );
		cbDesc.Usage = D3D10_USAGE_IMMUTABLE;
		cbDesc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
		cbDesc.CPUAccessFlags = 0;
		cbDesc.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA InitData;
		InitData.pSysMem = &cbConstant;
		InitData.SysMemPitch = 0;
		InitData.SysMemSlicePitch = 0;

		V( pDEV->CreateBuffer( &cbDesc, &InitData, &pConstantBuffercbConstant ) );

		//--------------------------------------------------------------------------------------
		// Create the effect
		//--------------------------------------------------------------------------------------
		DWORD dwShaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
		#if defined( DEBUG ) || defined( _DEBUG )
		// Set the D3D10_SHADER_DEBUG flag to embed debug information in the shaders.
		// Setting this flag improves the shader debugging experience, but still allows 
		// the shaders to be optimized and to run exactly the way they will run in 
		// the release configuration of this program.
		dwShaderFlags |= D3D10_SHADER_DEBUG;
		#else
		dwShaderFlags |= D3D10_SHADER_OPTIMIZATION_LEVEL3;
		#endif
		
		V( D3DX10CreateEffectFromFile( L"Sky.fx", NULL, NULL, "fx_4_0", dwShaderFlags, 0, pDEV, NULL, NULL, &pEffect, NULL, NULL ) );
		// Obtain the techniques
		pTechniqueRender = pEffect->GetTechniqueByName( "Render" );
		pTechniqueUpdate = pEffect->GetTechniqueByName( "Update" );
		pTechniqueUpdateFast = pEffect->GetTechniqueByName( "Update_Fast" );
		pTechniqueCreateOpticalDepth = pEffect->GetTechniqueByName( "Create_Optical_Depth" );
		// Obtain the variables
		pWorldViewProjVar = pEffect->GetVariableByName( "WorldViewProjection" )->AsMatrix();
		pv3SunDirVar =  pEffect->GetVariableByName( "v3SunDir" )->AsVector();
		pMieVar = pEffect->GetVariableByName( "txMie" )->AsShaderResource();
		pRayleighVar = pEffect->GetVariableByName( "txRayleigh" )->AsShaderResource();
		pOpticalDepthVar = pEffect->GetVariableByName( "txOpticalDepth" )->AsShaderResource();
		pConstantBuffercbConstantVar = pEffect->GetConstantBufferByName("cbConstant")->AsConstantBuffer();
		
		// Bind cbuffer
		pConstantBuffercbConstantVar->SetConstantBuffer( pConstantBuffercbConstant );
		pv3SunDirVar->SetFloatVectorArray( &v3SunDir.x, 0, 3 );

		//--------------------------------------------------------------------------------------
		// Create Textures
		//--------------------------------------------------------------------------------------
		//DXGI_FORMAT_R11G11B10_FLOAT
		//DXGI_FORMAT_R16G16B16A16_FLOAT
		//DXGI_FORMAT_R32G32B32A32_FLOAT

		const DXGI_FORMAT Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
		DXGI_SAMPLE_DESC SDesc = { 1, 0 };
		D3D10_TEXTURE2D_DESC Tdesc = { RayleighMieN, RayleighMieN / 2, 1, 1, Format, SDesc, D3D10_USAGE_DEFAULT,
									   D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE, 0, 0 };

		V( pDEV->CreateTexture2D( &Tdesc, NULL, (ID3D10Texture2D **)&pMie ) );
		V( pDEV->CreateTexture2D( &Tdesc, NULL, (ID3D10Texture2D **)&pRayleigh ) );

		Tdesc.Width = Tdesc.Height = OpticalDepthN;
		V( pDEV->CreateTexture2D( &Tdesc, NULL, (ID3D10Texture2D **)&pOpticalDepth ) );

		//--------------------------------------------------------------------------------------
		// Create Shader-Resource View & Render-Target View
		//--------------------------------------------------------------------------------------
		V( pDEV->CreateShaderResourceView( pMie, NULL, &pMieSRV ) );
		V( pDEV->CreateShaderResourceView( pRayleigh, NULL, &pRayleighSRV ) );
		V( pDEV->CreateShaderResourceView( pOpticalDepth, NULL, &pOpticalDepthSRV ) );

		V( pDEV->CreateRenderTargetView( pMie, NULL, &pMieRTV ) );
		V( pDEV->CreateRenderTargetView( pRayleigh, NULL, &pRayleighRTV ) );
		V( pDEV->CreateRenderTargetView( pOpticalDepth, NULL, &pOpticalDepthRTV ) );


		//--------------------------------------------------------------------------------------
		// Create RT vertex buffer 
		//--------------------------------------------------------------------------------------
		RTVertex Quad[] =
		{
			{ D3DXVECTOR2( -1.0, -1.0 ) },
			{ D3DXVECTOR2( -1.0,  1.0 ) },
			{ D3DXVECTOR2(  1.0, -1.0 ) },
			{ D3DXVECTOR2(  1.0,  1.0 ) },
		};

		D3D10_BUFFER_DESC bufferDesc;
		bufferDesc.Usage            = D3D10_USAGE_IMMUTABLE;
		bufferDesc.ByteWidth        = sizeof( RTVertex ) * 4;
		bufferDesc.BindFlags        = D3D10_BIND_VERTEX_BUFFER;
		bufferDesc.CPUAccessFlags   = 0;
		bufferDesc.MiscFlags        = 0;

		InitData.pSysMem = Quad;
		InitData.SysMemPitch = 0;
		InitData.SysMemSlicePitch = 0;
		V( pDEV->CreateBuffer( &bufferDesc, &InitData, &pVertexBufferRT ) );

		//--------------------------------------------------------------------------------------
		// Create RT layout
		//--------------------------------------------------------------------------------------
		D3D10_INPUT_ELEMENT_DESC RT_layout[] =
		{
			{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0,  0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		};

		// Create the input layout
		D3D10_PASS_DESC PassDesc;
		pTechniqueUpdate->GetPassByIndex( 0 )->GetDesc( &PassDesc );
		V( pDEV->CreateInputLayout( RT_layout, sizeof(RT_layout) / sizeof( RT_layout[0] ), PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &pRTVertexLayout ) );

		CreateDome();
		MakeGPUOpticalDepth();
	}

	~CSky()
	{
		SAFE_RELEASE( pDome );	
		SAFE_RELEASE( pEffect );
		SAFE_RELEASE( pConstantBuffercbConstant );
		SAFE_RELEASE( pVertexBufferRT );

		SAFE_RELEASE( pDomeVertexLayout );
		SAFE_RELEASE( pRTVertexLayout );

		SAFE_RELEASE( pMie );
		SAFE_RELEASE( pRayleigh );
		SAFE_RELEASE( pOpticalDepth );

		SAFE_RELEASE( pMieSRV );
		SAFE_RELEASE( pRayleighSRV );
		SAFE_RELEASE( pOpticalDepthSRV );

		SAFE_RELEASE( pMieRTV );
		SAFE_RELEASE( pRayleighRTV );
		SAFE_RELEASE( pOpticalDepthRTV );
	}

	void SetSunTime( double dTime )
	{
		if(dTime >=0)
			v3SunDir = D3DXVECTOR3( sin(D3DX_PI*dTime), cos(D3DX_PI*dTime), 0 );
		else
			v3SunDir = D3DXVECTOR3( sin(D3DX_PI*0.5), cos(D3DX_PI*0.5), 0 );

		pv3SunDirVar->SetFloatVectorArray( &v3SunDir.x, 0, 3 );
	}

	void Render( D3DXMATRIX ModelView, const D3DXMATRIX & Projection )
	{
		//pMieVar->SetResource( NULL );
		//pRayleighVar->SetResource( NULL );

		// Hack, Unbound textures in shader when used as rendertarget
		ID3D10ShaderResourceView * pSRVnull[3] = { NULL, NULL, NULL };
		pDEV->PSSetShaderResources(0, 3, pSRVnull );

		pOpticalDepthVar->SetResource( pOpticalDepthSRV );
		MakeGPUMieRayleighBuffer();

		ModelView._41 = ModelView._42 = ModelView._43 = 0;

		D3DXMATRIX yRotate;
		D3DXMatrixRotationY(
			&yRotate,
			-D3DX_PI*0.5
		);
		ModelView = yRotate * ModelView;

		D3DXMATRIX MP = ModelView * Projection;
		pWorldViewProjVar->SetMatrix( (float*)&MP );
		//pHGVar->SetFloatVectorArray( &HG.x, 0, 3 );
		//pSunDirVar->SetFloatVectorArray( &Sun.x, 0, 3 );
		
		pMieVar->SetResource( pMieSRV );
		pRayleighVar->SetResource( pRayleighSRV );
		pOpticalDepthVar->SetResource( pOpticalDepthSRV );

		// Set the input layout
		pDEV->IASetInputLayout( pDomeVertexLayout );

		D3D10_TECHNIQUE_DESC techDesc;
		pTechniqueRender->GetDesc( &techDesc );
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			pTechniqueRender->GetPassByIndex( p )->Apply(0);
			pDome->DrawSubset(0);
		}
	}	

	Vec2f GetDensityRatio( const double & dHeight ) const
	{
		Vec2f DensityRatio;

		const double dAltitude = (dHeight - cbConstant.InnerRadius) * cbConstant.fScale;
		//dAltitude = max(dAltitude, 0.0);
		DensityRatio.r = exp( -dAltitude / cbConstant.v2dRayleighMieScaleHeight.x );
		DensityRatio.m = exp( -dAltitude / cbConstant.v2dRayleighMieScaleHeight.y );
		return DensityRatio;
	}

	void t( Vec2f & OpticalDepth, D3DXVECTOR3 P, D3DXVECTOR3 Px )
	{
		// Out Scattering Integral / OpticalDepth
		OpticalDepth.r = OpticalDepth.m = 0;

		D3DXVECTOR3 v3Vector =  Px - P;
		float dFar = D3DXVec3Length( &v3Vector );
		D3DXVECTOR3 v3Dir = v3Vector / dFar;
			
		float dSampleLength = dFar / cbConstant.tNumSamples;
		float dScaledLength = dSampleLength * cbConstant.fScale;
		//double dScaledLength = 1.0 / double(tNumSamples);
		D3DXVECTOR3 vSampleRay = v3Dir * dSampleLength;
		P += vSampleRay * 0.5f;
			
		for(int i = 0; i < cbConstant.tNumSamples; i++)
		{
			float dHeight = D3DXVec3Length( &P );
			Vec2f POpticalDepth = GetDensityRatio( dHeight );
			OpticalDepth.r += POpticalDepth.r; 
			OpticalDepth.m += POpticalDepth.m; 
			P += vSampleRay;
		}		

		OpticalDepth.r *= dScaledLength;
		OpticalDepth.m *= dScaledLength;
	}

	void MakeGPUOpticalDepth()
	{
		// Save RT & DS config
		const int MaxRTV = 1;
		ID3D10RenderTargetView * RTVdef[MaxRTV];
		ID3D10DepthStencilView * DSVdef = NULL;
		ZeroMemory( RTVdef, MaxRTV * sizeof(ID3D10RenderTargetView) );
		pDEV->OMGetRenderTargets( MaxRTV, RTVdef, &DSVdef );

		// Set new RT
		ID3D10RenderTargetView * MRT[1] = { pOpticalDepthRTV };
		pDEV->OMSetRenderTargets( 1, MRT, NULL );

		// Save Viewport
		D3D10_VIEWPORT SaveVp[1];
		UINT NumVp[1] = { 1 };
		pDEV->RSGetViewports( NumVp , SaveVp );

		D3D10_VIEWPORT vp[1];
		vp[0].Width = float(OpticalDepthN);
		vp[0].Height = float(OpticalDepthN);
		vp[0].MinDepth = 0;
		vp[0].MaxDepth = 1;
		vp[0].TopLeftX = 0;
		vp[0].TopLeftY = 0;
		pDEV->RSSetViewports( 1, vp );

		// Draw
		ID3D10Buffer * pVertexBuffer[2] = { pVertexBufferRT, NULL };
		UINT strides[1] = { sizeof( RTVertex ) };
		UINT offsets[1] = { 0 };

		// Set the input layout
		pDEV->IASetInputLayout( pRTVertexLayout );
		pConstantBuffercbConstantVar->SetConstantBuffer( pConstantBuffercbConstant );

		D3D10_TECHNIQUE_DESC techDesc;
		pTechniqueCreateOpticalDepth->GetDesc( &techDesc );
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			pTechniqueCreateOpticalDepth->GetPassByIndex( p )->Apply(0);

			pDEV->IASetVertexBuffers( 
				0,                 //first input slot for binding
				1,                 //number of buffers in the array
				&pVertexBuffer[0], //array of vertex buffers
				&strides[0],       //array of stride values, one for each buffer
				&offsets[0] );     //array of offset values, one for each buffer

			pDEV->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
			pDEV->Draw( 4, 0 );
		}

		pDEV->IASetVertexBuffers( 
				0,                 //first input slot for binding
				1,                 //number of buffers in the array
				&pVertexBuffer[1], //array of vertex buffers
				&strides[0],       //array of stride values, one for each buffer
				&offsets[0] );     //array of offset values, one for each buffer
		
		// Set Old Viewport
		pDEV->RSSetViewports( 1, SaveVp );

		// Set Old RT & DS config
		pDEV->OMSetRenderTargets( MaxRTV, RTVdef, DSVdef );

		for( int i = 0; i < MaxRTV; i++ )
			SAFE_RELEASE( RTVdef[i] );
		SAFE_RELEASE( DSVdef );
/*
		D3DX10SaveTextureToFile(
		  pOpticalDepth,
		  D3DX10_IFF_DDS,
		  L"OpticalDepth.dds"
		);
		*/
	}

	void MakeGPUMieRayleighBuffer()
	{
		// Save RT & DS config
		const int MaxRTV = 2;
		ID3D10RenderTargetView * RTVdef[MaxRTV];
		ID3D10DepthStencilView * DSVdef = NULL;
		ZeroMemory( RTVdef, MaxRTV * sizeof(ID3D10RenderTargetView) );
		pDEV->OMGetRenderTargets( MaxRTV, RTVdef, &DSVdef );

		// Set new RT
		ID3D10RenderTargetView * MRT[2] = { pRayleighRTV, pMieRTV };
		pDEV->OMSetRenderTargets( 2, MRT, NULL );

		// Save Viewport
		D3D10_VIEWPORT SaveVp[1];
		UINT NumVp[1] = { 1 };
		pDEV->RSGetViewports( NumVp , SaveVp );

		D3D10_VIEWPORT vp[1];
		vp[0].Width = float(RayleighMieN);
		vp[0].Height = float(RayleighMieN / 2);
		vp[0].MinDepth = 0;
		vp[0].MaxDepth = 1;
		vp[0].TopLeftX = 0;
		vp[0].TopLeftY = 0;
		pDEV->RSSetViewports( 1, vp );

		// Draw
		ID3D10Buffer * pVertexBuffer[2] = { pVertexBufferRT, NULL };
		UINT strides[1] = { sizeof( RTVertex ) };
		UINT offsets[1] = { 0 };

		// Set the input layout
		pDEV->IASetInputLayout( pRTVertexLayout );
		pConstantBuffercbConstantVar->SetConstantBuffer( pConstantBuffercbConstant );

		D3D10_TECHNIQUE_DESC techDesc;
		pTechniqueUpdate->GetDesc( &techDesc ); // method 1
		//pTechniqueUpdateFast->GetDesc( &techDesc ); // method 2
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			pTechniqueUpdate->GetPassByIndex( p )->Apply(0);
			//pTechniqueUpdateFast->GetPassByIndex( p )->Apply(0);

			pDEV->IASetVertexBuffers( 
				0,                 //first input slot for binding
				1,                 //number of buffers in the array
				&pVertexBuffer[0], //array of vertex buffers
				&strides[0],       //array of stride values, one for each buffer
				&offsets[0] );     //array of offset values, one for each buffer

			pDEV->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );
			pDEV->Draw( 4, 0 );
		}

		// Unbind vertex buffer
		pDEV->IASetVertexBuffers( 
				0,                 //first input slot for binding
				1,                 //number of buffers in the array
				&pVertexBuffer[1], //array of vertex buffers
				&strides[0],       //array of stride values, one for each buffer
				&offsets[0] );     //array of offset values, one for each buffer
		
		// Set Old Viewport
		pDEV->RSSetViewports( 1, SaveVp );

		// Set Old RT & DS config
		pDEV->OMSetRenderTargets( MaxRTV, RTVdef, DSVdef );

		for( int i = 0; i < MaxRTV; i++ )
			SAFE_RELEASE( RTVdef[i] );
		SAFE_RELEASE( DSVdef );
/*
		D3DX10SaveTextureToFile(
		  pMie,
		  D3DX10_IFF_DDS,
		  L"Mie.dds"
		);

		D3DX10SaveTextureToFile(
		  pRayleigh,
		  D3DX10_IFF_DDS,
		  L"Rayleigh.dds"
		);
		*/
	}
};

typedef CSky Sky;